/*
 * File: DataHarvester.java
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package tvseries.imdb;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import tvseries.entity.Episode;
import tvseries.entity.Series;
import tvseries.utils.FileUtil;
import tvseries.utils.HttpUtil;
import tvseries.utils.PropUtil;

/**
 * Class for screen scraping data off IMDB.
 * 
 * @author nish.naidoo@gmail.com
 * 
 */
public class DataHarvester
{
	private static final Logger log = Logger.getLogger(DataHarvester.class);

	private HttpUtil httpUtils = null;
	private Map<String, Pattern> regex = null;

	public DataHarvester()
	{
		httpUtils = HttpUtil.getInstance();
		regex = buildPatterns();
	}

	public Series getSeriesData(String imdbId, String title) throws Exception
	{
		String url = PropUtil.get("imdb.url.base") + imdbId + PropUtil.get("imdb.url.plotsummary");
		String htmlContent = httpUtils.get(url);

		String summary = extractData("seriesSummary", htmlContent);

		Series series = new Series();
		series.setId(imdbId);
		series.setImdbUrl(PropUtil.get("imdb.url.base") + imdbId);
		series.setSummary(summary);
		series.setTitle(title);

		return series;
	}

	public Set<Episode> getEpisodeData(Series series) throws Exception
	{
		String url = PropUtil.get("imdb.url.base") + series.getId() + PropUtil.get("imdb.url.episodes");
		String htmlContent = httpUtils.get(url);

		Pattern p = regex.get("validCheck");

		Set<Episode> episodes = new TreeSet<Episode>();

		Scanner scanner = new Scanner(htmlContent);
		while (scanner.hasNextLine())
		{
			String data = scanner.nextLine();
			Matcher m = p.matcher(data);
			if (!m.matches())
				continue;

			try
			{
				int episode = Integer.parseInt(extractData("episodeNumber", data));

				// Filter out unwanted episodes
				if (episode == 0)
					continue;

				int season = Integer.parseInt(extractData("season", data));

				String title = extractData("title", data);
				if (title == null)
				{
					title = "[No Title Available]";
					if (log.isDebugEnabled())
						log.debug("No Title: " + season + "." + episode);
				}

				if (title.startsWith("Episode #"))
					continue;

				String episodeId = extractData("episodeId", data);

				Episode e = new Episode();
				e.setId(episodeId);
				e.setTitle(title);
				e.setSeason(season);
				e.setEpisode(episode);
				e.setSummary(extractData("summary", data));
				e.setImdbUrl(PropUtil.get("imdb.url.site") + extractData("imdbUrl", data));
				e.setReleaseYear(extractData("year", data));
				e.setAirDate(extractData("airDate", data));

				if (FileUtil.exists(series, e))
					e.setStatus("Available");
				else
					e.setStatus("Missing");

				episodes.add(e);
			}
			catch (NumberFormatException nfe)
			{
				log.warn("Unable to parse:\n" + data);
			}
		}

		return episodes;
	}

	private String extractData(String field, String data)
	{
		if (log.isDebugEnabled())
			log.debug("pattern matching: [" + field + "]");

		// Special case for series summary. Takes too long otherwise.
		if (field.equals("seriesSummary") && data.indexOf("<p class=\"plotpar\">") == -1)
		{
			if (log.isDebugEnabled())
				log.debug("pattern matching complete: [ no plotpar found]");

			return null;
		}

		Pattern p = regex.get(field);
		Matcher m = p.matcher(data);

		boolean found = m.find();

		if (log.isDebugEnabled())
			log.debug("pattern matching: [" + field + "] complete.");

		if (!found || m.groupCount() == 0)
			return null;

		String result = m.group(1).trim();

		if (result == null || "".equals(result.trim()))
			return null;

		return result;
	}

	private Map<String, Pattern> buildPatterns()
	{
		Map<String, Pattern> regex = new HashMap<String, Pattern>();
		String key = null;
		Pattern p = null;

		key = "validCheck";
		p = Pattern.compile("<div class=\"filter-all filter-year.*");
		regex.put(key, p);

		key = "episodeId";
		p = Pattern.compile(".*?<a href=\\\"/title/tt(.*?)/\\\">.*?");
		regex.put(key, p);

		key = "year";
		p = Pattern.compile(".*?<div class=\\\"filter-all filter-year-(\\w*?)\\\">.*?");
		regex.put(key, p);

		key = "season";
		p = Pattern.compile(".*?Season (\\d+?),.*?");
		regex.put(key, p);

		key = "episodeNumber";
		p = Pattern.compile(".*?Episode (\\d+?):.*?");
		regex.put(key, p);

		key = "imdbUrl";
		p = Pattern.compile(".*?<a href=\\\"(.*?)\\\">.*?");
		regex.put(key, p);

		key = "title";
		p = Pattern.compile(".*?<a href=\".*?\">(.*?)</a>.*?");
		regex.put(key, p);

		key = "airDate";
		p = Pattern.compile(".*?<span class=\\\"less-emphasis\\\">Original Air Date&mdash;<strong>(.*)</strong></span>.*?");
		regex.put(key, p);

		key = "summary";
		p = Pattern.compile(".*?<br>(.*?)</td>.*?");
		regex.put(key, p);

		key = "seriesSummary";
		p = Pattern.compile(".*?<p class=\"plotpar\">(.*?)<i>.*", Pattern.DOTALL);
		regex.put(key, p);

		return regex;
	}
}
